﻿#define _CRT_SECURE_NO_WARNINGS

/*********************************************************************/
//seg0 操作系统和平台编译器等的识别
/*********************************************************************/
//【原生识别操作系统和编译器】
//操作系统的宏(windows)    _WINDOWS
//操作系统的宏(linux)      __linux__
//操作系统的宏(android)    __linux__    __ANDROID__
//操作系统的宏(mac)        __APPLE__

//编译器的宏(msvc)         MS VC++ 14.0 _MSC_VER = 1900(VS2015)
//编译器的宏(msvc)         MS VC++ 12.0 _MSC_VER = 1800(VS2013)
//编译器的宏(msvc)         MS VC++ 11.0 _MSC_VER = 1700(VS2012)
//编译器的宏(msvc)         MS VC++ 10.0 _MSC_VER = 1600(VS2010)
//编译器的宏(msvc)         MS VC++ 9.0  _MSC_VER = 1500(VS2008)
//编译器的宏(msvc)         MS VC++ 8.0  _MSC_VER = 1400(VS2005)
//编译器的宏(msvc)         MS VC++ 7.1  _MSC_VER = 1310(VS2003)
//编译器的宏(msvc)         MS VC++ 7.0  _MSC_VER = 1300
//编译器的宏(msvc)         MS VC++ 6.0  _MSC_VER = 1200
//编译器的宏(GNUC)         __GNUC__
//编译器的宏(CLANG)        __GNUC__    __clang__

//  MSVC识别运行时示例：
//  #if defined (_DEBUG)  
//      #if defined(_DLL)
//          printf("use MDd");
//      #else
//          printf("use MTd");
//      #endif
//  #else	/*(RELEASE)*/	
//      #if defined(_DLL)
//          printf("use MD");
//      #else
//          printf("use MT");
//      #endif
//  #endif

//【PCLIB识别操作系统和编译器】
//操作系统的宏(windows)    PCOS_WIN
//操作系统的宏(linux)      PCOS_LINUX
//操作系统的宏(android)    PCOS_LINUX    PCOS_ANDROID
//操作系统的宏(mac)        PCOS_MAC

//程序目标系统的宏(windows x86)    PCOS_WIN32    
//程序目标系统的宏(windows x64)    PCOS_WIN64
//程序目标系统的宏(mac x86)        PCOS_MAC32
//程序目标系统的宏(mac x64)        PCOS_MAC64
//程序目标系统的宏(ios)            PCOS_IOS
/*********************************************************************/
#if defined(__APPLE__) && (defined(__GNUC__) || defined(__xlC__) || defined(__xlc__) ) 
#define PCOS_MAC
#elif defined(__ANDROID__) || defined(ANDROID)
#define PCOS_ANDROID
#define PCOS_LINUX
#elif defined(__CYGWIN__)
#define PCOS_CYGWIN
#elif !defined(SAG_COM) && (!defined(WINAPI_FAMILY) || WINAPI_FAMILY==WINAPI_FAMILY_DESKTOP_APP) && (defined(WIN64) || defined(_WIN64) || defined(__WIN64__))
#define PCOS_WIN64
#elif !defined(SAG_COM) && (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__))
#if defined(WINCE) || defined(_WIN32_WCE)
#define PCOS_WINCE
#elif defined(WINAPI_FAMILY)
#if defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP
#define PCOS_WINPHONE
#define PCOS_WINRT
#else
#define PCOS_WINRT
#endif
#else
#define PCOS_WIN32
#endif
#elif defined(__sun) || defined(sun)
#define PCOS_SOLARIS
#elif defined(hpux) || defined(__hpux)
#define PCOS_HPUX
#elif defined(__ultrix) || defined(ultrix)
#define PCOS_ULTRIX
#elif defined(sinix)
#define PCOS_RELIANT
#elif defined(__native_client__)
#define PCOS_NACL
#elif defined(__linux__) || defined(__linux)
#define PCOS_LINUX
#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
#ifndef __FreeBSD_kernel__
#define PCOS_FREEBSD
#endif
#define PCOS_FREEBSD_KERNEL
#define PCOS_BSD4
#elif defined(__NetBSD__)
#define PCOS_NETBSD
#define PCOS_BSD4
#elif defined(__OpenBSD__)
#define PCOS_OPENBSD
#define PCOS_BSD4
#elif defined(__bsdi__)
#define PCOS_BSDI
#define PCOS_BSD4
#elif defined(__INTERIX)
#define PCOS_INTERIX
#define PCOS_BSD4
#elif defined(__sgi)
#define PCOS_IRIX
#elif defined(__osf__)
#define PCOS_OSF
#elif defined(_AIX)
#define PCOS_AIX
#elif defined(__Lynx__)
#define PCOS_LYNX
#elif defined(__GNU__)
#define PCOS_HURD
#elif defined(__DGUX__)
#define PCOS_DGUX
#elif defined(__QNXNTO__)
#define PCOS_QNX
#elif defined(_SEQUENT_)
#define PCOS_DYNIX
#elif defined(_SCO_DS)
#define PCOS_SCO
#elif defined(__USLC__) 
#define PCOS_UNIXWARE
#elif defined(__svr4__) && defined(i386)
#define PCOS_UNIXWARE
#elif defined(__INTEGRITY)
#define PCOS_INTEGRITY
#elif defined(VXWORKS)
#define PCOS_VXWORKS
#elif defined(__HAIKU__)
#define PCOS_HAIKU
#elif defined(__MAKEDEPEND__)
#else
#error    "THIS OS NOT SUPPORTETD."
#endif
#if defined(PCOS_WIN32) || defined(PCOS_WIN64) || defined(PCOS_WINCE) || defined(PCOS_WINRT) || defined(Q_OS_WIN) || defined(Q_CC_MSVC) 
#define PCOS_WIN
#if !defined(_WINDOWS)
#define _WINDOWS
#endif
#endif
#if defined(PCOS_MAC)
#include <TargetConditionals.h>
#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
#define PCOS_IOS
#elif defined(TARGET_OS_MAC) && TARGET_OS_MAC
#if defined(__LP64__) 
#define PCOS_MAC64
#else
#define PCOS_MAC32
#endif
#endif
#endif

///////////////////////////////////////////////////////////////////////////////
// 识别程序是32位还是64位
#ifndef PC_VAR_64BIT
#if defined(__LP64__) || (defined(__x86_64__) && defined(__ILP32__)) || defined(_WIN64) || defined(__EMSCRIPTEN__)
#define PC_VAR_64BIT 1
#else
#define PC_VAR_64BIT 0
#endif
#endif // PC_VAR_64BIT

///////////////////////////////////////////////////////////////////////////////
// 识别大小端
#define PC_LITTLEENDIAN  0   //!< 小端。如果PC_ENDIAN==PC_LITTLEENDIAN，则机器为小端
#define PC_BIGENDIAN     1   //!< 大端。如果PC_ENDIAN==PC_BIGENDIAN，则机器为大端

//! 机器大小端
/*!
    GCC 4.6提供了用于检测目标计算机字节序的宏。 但是其他
     编译器可能没有这个。 用户可以将PC_ENDIAN定义为
     PC_LITTLEENDIAN或PC_BIGENDIAN。

    参考：
    \li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html
    \li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp
*/
#ifndef PC_ENDIAN
// 使用GCC 4.6的宏进行检测
#  ifdef __BYTE_ORDER__
#    if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#      define PC_ENDIAN PC_LITTLEENDIAN
#    elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#      define PC_ENDIAN PC_BIGENDIAN
#    else
#      error Unknown machine endianess detected. User needs to define PC_ENDIAN.
#    endif // __BYTE_ORDER__
// 使用GLIBC的endian.h进行检测
#  elif defined(__GLIBC__)
#    include <endian.h>
#    if (__BYTE_ORDER == __LITTLE_ENDIAN)
#      define PC_ENDIAN PC_LITTLEENDIAN
#    elif (__BYTE_ORDER == __BIG_ENDIAN)
#      define PC_ENDIAN PC_BIGENDIAN
#    else
#      error Unknown machine endianess detected. User needs to define PC_ENDIAN.
#   endif // __GLIBC__
// 使用_LITTLE_ENDIAN和_BIG_ENDIAN宏进行检测
#  elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)
#    define PC_ENDIAN PC_LITTLEENDIAN
#  elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)
#    define PC_ENDIAN PC_BIGENDIAN
// 使用架构宏进行检测
#  elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__)
#    define PC_ENDIAN PC_BIGENDIAN
#  elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__)
#    define PC_ENDIAN PC_LITTLEENDIAN
#  elif defined(_MSC_VER) && defined(_M_ARM)
#    define PC_ENDIAN PC_LITTLEENDIAN
#  elif defined(PC_DOXYGEN_RUNNING)
#    define PC_ENDIAN
#  else
#    error Unknown machine endianess detected. User needs to define PC_ENDIAN.   
#  endif
#endif // PC_ENDIAN


//休眠指定毫秒数，android下有被打断的可能，使用时注意
#if defined(_MSC_VER) && _MSC_VER < 1800
#define PCSleep(_MSEC)    ::Sleep(_MSEC)
#else
#define PCSleep(_MSEC)    std::this_thread::sleep_for(std::chrono::milliseconds(_MSEC))
#endif

//补齐window下的strcasecmp strncasecmp vsnprintf
#if defined(PCOS_WIN)
#ifndef strcasecmp
#define strcasecmp   _stricmp
#endif
#ifndef strncasecmp
#define strncasecmp  _strnicmp
#endif
#endif

//获取最后的错误码
#if defined(PCOS_WIN)
#define PCGetLastError()    GetLastError()
#else
#define PCGetLastError()    errno
#endif



#if defined(PCOS_WIN)
#else
#define ERROR_ALREADY_EXISTS   EEXIST
#endif
/*********************************************************************/
/*********************************************************************/

#include <functional>
#include <fstream>
#if defined(PCOS_WIN)  
    #include <io.h>
    #include <direct.h>
    #include <winerror.h>
    #include <stdarg.h>
    #include <Windows.h>
#else
    #include <stdarg.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sys/syscall.h>
#endif

#include "ZL_TLogger.h"

class TUtil
{
public:
    /**
    *@brief     将字符串去掉前/后的特定字符(一般用于去空格 )
    *@param     src        `[in]    原始串
    *@param     what        [in]    移除的字符 如" \n\t\r"则表示前后移除这四个字符
    *@param     nSrcLen     [in]    原始串长度。nSrcLen < 0时，认为src为c字符串
    *@return    结果字符串
    */
    static std::string PCStrTrim(const char* src, const char* what, int nSrcLen = -1)
    {
        if (NULL == src || src[0] == 0)
        {
            return "";
        }
        std::string result;
        if (nSrcLen < 0)
            result = src;
        else
            result = result.assign(src, nSrcLen);
        PCStrTrimInplace(result, what);
        return result;
    }

    /**
    *@brief     将字符串去掉前/后的特定字符(一般用于去空格 ) 原地替换
    *@param     src        `[in/out]    原始串
    *@param     what        [in]        移除的字符 如" \n\t\r"则表示前后移除这四个字符
    *@return    结果字符串
    */
    static void PCStrTrimInplace(std::string& src, const char* what)
    {
        if (NULL == what || what[0] == 0)
        {
            return;
        }
        src.erase(0, src.find_first_not_of(what));
        src.erase(src.find_last_not_of(what) + 1);
    }

    /**
    *@brief     读取文件时，智能跳过BOM。处理文本文件才需要这样做
    *@param     fin[in]    已经打开的输入文件流
    *@return    int [文件类型] (根据前几个字节文件头判断的结果。仅供参考)
    *@note      此函数会移动文件指针
    */
#define PC_TEXT_ANSI           (0)       //[记事本ANSI]                  无BOM
#define PC_TEXT_UCS2_LE_BOM    (1)       //[记事本Unicode]               FF FE
#define PC_TEXT_UCS2_BE_BOM    (2)       //[记事本Unicode big endian]    FE FF    
#define PC_TEXT_UTF8_BOM       (3)       //[记事本UTF-8]                 EF BB BF
#define PC_TEXT_UTF8           (4)       //[]                            无BOM
    static int PCSkipBOM(std::ifstream& fin)
    {
        if (!fin.good())
        {
            return -1;
        }
        char szHead[3] = { 0 };
        fin.read(szHead, sizeof(szHead));
        std::streamsize count = fin.gcount();
        if (count >= 2)
        {
            if (szHead[0] == '\xFF' && szHead[1] == '\xFE')
            {
                fin.seekg(2, fin.beg);
                return (PC_TEXT_UCS2_LE_BOM);
            }
            if (szHead[0] == '\xFE' && szHead[1] == '\xFF')
            {
                fin.seekg(2, fin.beg);
                return (PC_TEXT_UCS2_BE_BOM);
            }
            if (count == 3 && szHead[0] == '\xEF' && szHead[1] == '\xBB' && szHead[2] == '\xBF')
            {
                fin.seekg(3, fin.beg);
                return (PC_TEXT_UTF8_BOM);
            }
        }
        fin.seekg(0, fin.beg);
        return (PC_TEXT_ANSI);
    }

    /**
    *@brief     按行‘\n’扫描操作文本文件,每扫描到一行，执行回调函数。
    *@param     pszPath     [in]    路径。
    *@param     callback    [in]    回调函数。回调函数返回失败时将会退出遍历
    *@param     lineEndChar [in]    line end char
    *@return    是否成功
    */
    static bool PCScanFileLine(const char* pszPath,
        std::function<void(std::string& strLine)> callback,
        const char  lineEndChar = '\n')
    {
        //打开文件
        std::ifstream fin(pszPath);
        if (!fin.is_open())
        {
            return false;
        }

        auto result2 = PCSkipBOM(fin);
        if (-1 == result2)
        {
            fin.close();
            return false;
        }
        if (result2 == PC_TEXT_UCS2_LE_BOM || result2 == PC_TEXT_UCS2_BE_BOM)
        {
            fin.close();
            return false;
        }

        //逐行读取并解析文件
        std::string    strLine;
        while (std::getline(fin, strLine, lineEndChar))
        {
            if (callback)
            {
                callback(strLine);
            }
        }
        fin.close();
        return true;
    }

    /**
    *@brief     将一个全路径分离成目录名和文件名。如将"c:\a\1.txt"分离成"c:\a\"和1.txt
    *@param     pszSrcFullPath[in]    输入的包括文件名的全路径。路径分隔符支持\和/
    *@return    std::pair.first[目录名]    std::pair.second[文件名]
    */
    static std::pair<std::string, std::string>  PCSeperatePath(const char* pszSrcFullPath)
    {
        std::pair<std::string, std::string>  result;
        auto result2 = PCPathCheck(pszSrcFullPath);
        if (!result2)
        {
            return std::make_pair("", "");
        }

        //未找到\\或/则认为是纯文件名
        if (PCStrStri(pszSrcFullPath, "/") == NULL &&
            PCStrStri(pszSrcFullPath, "\\") == NULL)
        {
            return std::make_pair("", pszSrcFullPath);
        }

        //从后到前找分隔符
        int nPathLen = (int)strlen(pszSrcFullPath);
        for (int i = nPathLen - 1; i >= 0; i--)
        {
            if (pszSrcFullPath[i] == '\\' || pszSrcFullPath[i] == '/')
            {
                std::string dir(pszSrcFullPath, i + 1);
                std::string filename(pszSrcFullPath + i + 1, nPathLen - i - 1);
                return std::make_pair(std::move(dir), std::move(filename));
            }
            if (pszSrcFullPath[i] == ':' && i == nPathLen - 1)
            {
                std::string strPath = pszSrcFullPath;
                strPath += "/";
                return std::make_pair(std::move(strPath), "");
            }
        }
        return  std::make_pair("", pszSrcFullPath);
    }

    /**
    *@brief     检查路径是否存在，不存在则创建目录。
    *@param     pszFilePath    [in]        文件全路径。
    *@return    是否成功
    */
    static bool PCCheckAndCreateDir(const char* pszFilePath)
    {
        auto result2 = PCSeperatePath(pszFilePath);
        return PCCreateDir(result2.first.c_str());
    }

    /**
    *@brief     创建目录，如果子目录不存在，会依次创建
    *@param     pszPath    [in]    路径。
    *@return    成功：CPCResult.Succ    失败：CPCResult.ErrDesc[错误描述]
    */
    static bool PCCreateDir(const char* pszPath)
    {
        if (pszPath == NULL)
        {
            return false;
        }
        if (pszPath[0] == 0)
            return true;

        size_t nPathLen = strlen(pszPath);
        if (nPathLen >= 260)
        {
            return false;
        }

        //尝试直接创建，看能不能成功
        char pszCheckPath[260 + 2] = { 0 };
        memset(pszCheckPath, 0, sizeof(pszCheckPath));
        strncpy(pszCheckPath, pszPath, sizeof(pszCheckPath));
        if (pszCheckPath[nPathLen - 1] == '/')
        {
            pszCheckPath[nPathLen - 1] = '\0';
        }
        auto result = PCMakeDir(pszCheckPath);
        if (result)
        {
            return result;
        }

        //从前向后创建
        char* p = pszCheckPath + 1;
        while (1)
        {
            while (*p && *p != '\\' && *p != '/')
                p++;
            char hold = *p;
            *p = 0;

            result = PCMakeDir(pszCheckPath);
            if (!result)
            {
                return result;
            }

            if (hold == 0)
                break;
            *p++ = hold;
        }
        return result;
    }

    /**
    *@brief        创建0777权限的文件夹
    */
    static bool PCMakeDir(const char* filename)
    {
        //如果存在，直接返回成功
#if defined(PCOS_WIN)
        if (::_access(filename, 0) == 0)
        {
            return true;
        }
        int nMakeRet = ::_mkdir(filename);
#else
        if (::access(filename, 0) == 0)
        {
            return true;
        }
        //S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH
        //成功0 失败-1
        ::umask(0);
        int nMakeRet = ::mkdir(filename, 0777);
#endif
        if (nMakeRet != 0)
        {
            return false;
        }
        return true;
    }

    /**
    *@brief     初步检查一个路径是不是合法的
    *@param     pszSrcFullPath    [in]    输入的包括文件名的全路径。路径分隔符支持\和/
    *@return    成功：CPCResult.Succ    失败：CPCResult.ErrDesc[错误描述]
    */
    static bool PCPathCheck(const char* path)
    {
        if (path == NULL)
        {
            return false;
        }
        if (path[0] == 0)
        {
            return false;
        }
        size_t nPathLen = strlen(path);
        if (nPathLen >= 260)
        {
            return false;
        }
        return true;
    }

    /**
    *@brief        不区分大小写的strstr函数
    */
    static const char* PCStrStri(const char* str, const char* subStr)
    {
        if (str == NULL || subStr == NULL ||
            str[0] == 0 || subStr[0] == 0)
        {
            return NULL;
        }
        size_t lenSub = strlen(subStr);
        while (*str)
        {
            if (strncasecmp(str, subStr, lenSub) == 0)
            {
                return str;
            }
            ++str;
        }
        return NULL;
    }

    /**
    *@brief     取当前内核线程id，用了TLS，一个线程只会取一次
    *@return    当前线程id
    */
    static int PCGetTID()            
    {
#if defined(PCOS_WIN)
        return (int)::GetCurrentThreadId();
#else
        static __thread int s_threadid = -1;
        if (s_threadid != -1)
            return s_threadid;
        s_threadid = ::syscall(SYS_gettid);
        return s_threadid;
#endif
    }

    /**
    *@brief      对于文件夹的路径格式，如果最后一个字符不是'/''\'，则添加'/'或'\'
    */
    static void PCDirFormat(char* szPath)
    {
        if (szPath == NULL || szPath[0] == 0)
            return;
        size_t nLastCharPos = strlen(szPath) - 1;
        if (nLastCharPos > 0 && szPath[nLastCharPos] != '\\' && szPath[nLastCharPos] != '/')
        {
#if defined(PCOS_WIN)
            szPath[nLastCharPos + 1] = '\\';
#else
            szPath[nLastCharPos + 1] = '/';
#endif
            szPath[nLastCharPos + 2] = 0;
        }
    }

    /**
    *@brief      对于文件夹的路径格式，如果最后一个字符不是'/''\'，则添加'/'或'\'
    */
    static void PCDirFormat(std::string& strPath)
    {
        if (strPath.empty())
            return;
        size_t nLastCharPos = strPath.length() - 1;
        if (nLastCharPos > 0 && strPath[nLastCharPos] != '\\' && strPath[nLastCharPos] != '/')
        {
#if defined(PCOS_WIN)
            strPath += '\\';
#else
            strPath += '/';
#endif
        }
    }
    
    /**
    *@brief     取当前时间
    *@return    是否成功
    */
    static bool GetLocalTM(struct tm& tmValue, unsigned int& uMsValue)
    {
        //获取当前时间
        auto llTimeMsValue = std::chrono::duration_cast<std::chrono::milliseconds>
            (std::chrono::system_clock::now().time_since_epoch()).count();

        //分离成秒/毫秒
        time_t nSec = llTimeMsValue / 1000;
        uMsValue = llTimeMsValue % 1000;

        //将time_t转换为tm结构体
#if defined(PCOS_WIN)
        if (0 != localtime_s(&tmValue, &nSec))
#else
        if (NULL == localtime_r(&nSec, &tmValue))
#endif
        {
            return false;
        }
        return true;
    }

    /**
    *@brief        从函数全名称里面提取出函数实际名称,仅供参考
    */
    static const char* PCGetFuncNameShort(const char* szFunction)
    {
        if (szFunction == NULL) return "";
        int nFuncLen = (int)strlen(szFunction);

        int left = -1;
        for (int i = nFuncLen - 1; i >= 0; i--)
        {
            if (szFunction[i] == ':')
            {
                left = i;
                break;
            }
        }
        if (left == -1)
            return szFunction;
        return szFunction + left + 1;
    }

    /**
    *@brief        从文件全名称里面提取实际文件名称，仅供参考
    */
    static const char* PCGetFileNameShort(const char* szFilePath)
    {
        if (szFilePath == NULL) return "";

        //从后到前找分隔符
        int nPathLen = (int)strlen(szFilePath);
        for (int i = nPathLen - 1; i >= 0; i--)
        {
            if (szFilePath[i] == '\\' || szFilePath[i] == '/')
            {
                if (i >= nPathLen - 1)
                    return szFilePath;
                return szFilePath + i + 1;
            }
            if (i == nPathLen - 1 && szFilePath[i] == ':')
            {
                return szFilePath;
            }
        }
        return szFilePath;
    }

    /**
    *@brief     将真实字节数组转换为十六进制显示的字符串，如"123"转换为"313233"
    *@param     src[in]            原始串
    *@param     src_len[in]        原始串长度
    *@param     delim[in]        转换后每个字节的分隔符
    *@param     bLowerCase[in]    转换后的十六进制数据的ABCDEF是大写还是小写
    *@param     nTypeLen[in]    src的数据类型长度，默认为byte（1），可选short（2）或int（4）
    *@return    转换后的结果串
    */
    static std::string  PCBytes2Dsp(const void* src, size_t src_len, const char* delim = "", bool bLowerCase = false, int nTypeLen = 1)
    {
        size_t delim_len = delim ? strlen(delim) : 0;
        if (src == NULL || src_len == 0 || delim == NULL || delim_len > 8)
        {
            return "";
        }
        if (nTypeLen != sizeof(unsigned char) && nTypeLen != sizeof(unsigned short) && nTypeLen != sizeof(unsigned int))
        {
            nTypeLen = sizeof(unsigned char);
        }

        std::string result;
        result.reserve(src_len * (2 + delim_len) + 2);
        char pszTmpBuf[12];
        for (size_t i = 0; i < src_len / nTypeLen; i++)
        {
            if (nTypeLen == sizeof(unsigned char))
            {
                sprintf(pszTmpBuf, bLowerCase ? "%02x" : "%02X", ((unsigned char*)src)[i]);
            }
            else if (nTypeLen == sizeof(unsigned short))
            {
                sprintf(pszTmpBuf, bLowerCase ? "%04x" : "%04X", ((unsigned short*)src)[i]);
            }
            else if (nTypeLen == sizeof(unsigned int))
            {
                sprintf(pszTmpBuf, bLowerCase ? "%08x" : "%08X", ((unsigned int*)src)[i]);
            }

            result += pszTmpBuf;
            result += delim;
        }
        return result;
    }

    /**
    *@brief     在某个年月上增加N个月，得到新的年月
    *@param     year[in/out]    输入年，输出新年
    *@param     month[in/out]   输入月，输出新月
    *@param     N[in]           增加N个月，负数代表减少-N个月
    *@return    是否成功
    */
    static bool AddMonth(int& year, int& month, int N)
    {
        if (year < 0 || year > 1000000 || month < 0 || month > 12)
            return false;
        int totalmonth = year * 12 + month + N - 1;
        year = totalmonth / 12;
        month = totalmonth % 12 + 1;
        return (year >= 0);
    }

    /**
    *@brief     在某个年月日上增加N天，得到新的年月日
    *@param     year[in/out]    输入年，输出新年
    *@param     month[in/out]   输入月，输出新月
    *@param     day[in/out]     输入日，输出新日
    *@param     N[in]           增加N天，负数代表减少-N天
    *@return    是否成功
    */
    static bool AddDay(int& year, int& month, int& day, int N)
    {
        bool bAdd = (N >= 0);
        int  leftN = N;
        while (1)
        {
            if (leftN == 0)
                break;
            int dayCount = TUtil::GetDays(year, month);
            if (dayCount == 0 || day < 0 || day > dayCount)
                return false;

            if (bAdd)
            {
                int maxAddDay = dayCount - day;
                if (maxAddDay >= leftN)
                {
                    //在本月就处理了
                    day += leftN;
                    break;
                }
                else
                {
                    //跨月
                    if (!AddMonth(year, month, 1))
                        return false;
                    day = 0;
                    leftN -= maxAddDay;
                }
            }
            else
            {
                int maxAddDay = day;
                if (maxAddDay > (-leftN))
                {
                    //在本月就处理了
                    day += leftN;
                    break;
                }
                else
                {
                    //跨月
                    if (!AddMonth(year, month, -1))
                        return false;
                    day = TUtil::GetDays(year, month);
                    leftN += maxAddDay;
                }
            }
        }

        return true;
    }

    /**
    *@brief     获取对应年月份的天数
    *@param     year    [in]    对应年份，支持范围【1800,】
    *@param     month   [in]    对应月份
    *@return    天数，不支持或参数错误返回0
    */
    static int GetDays(int year, int month)
    {
        if (year < 1800)
            return 0;
        switch (month)
        {
        case 1:case 3:case 5: case 7: case 8: case 10:case 12:
            return 31;
        case 4:case 6:case 9:case 11:
            return 30;
        case 2:
            return (TUtil::IsLeapYear(year) ? 29 : 28);
        default:
            return 0;
        }
    }

    /**
    *@brief     判断是否为闰年
    *@param     year[in]    年份
    *@return    闰年返回true，否则为false
    */
    static bool IsLeapYear(int year)
    {
        return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
    }
};

/**
*@brief  控制台颜色控制，对象析构后下一行自动还原
*@param  level  [IN]	红色警告，红色错误，其他黑色
*/
class CPCAutoConsoleColor 
{
public:
    CPCAutoConsoleColor(ZL_TLogLevl level)
        :m_eLevel(level)
    {
#if defined(PCOS_WIN)
        static const WORD LV_COLOR[] = { FOREGROUND_INTENSITY/*灰*/, 0, FOREGROUND_GREEN | FOREGROUND_RED/*黄*/, FOREGROUND_RED/*红*/, 0};
        if (level != ZL_TLogLevl::eINFO)
        {
            SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), LV_COLOR[(int)level]);
        }
#endif
    }
    ~CPCAutoConsoleColor()
    {
#if defined(PCOS_WIN)
        if(m_eLevel != ZL_TLogLevl::eINFO)
            SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE/*白*/);
#endif
    }
private:
    ZL_TLogLevl m_eLevel;
};

/**
*@brief     配置文件section辅助类，一般情况下用户无需直接使用
*           用户只需要使用下面的CPCConfig类
*/
class TConfigSection
{
public:
    //按照顺序排列的MAP
    typedef std::vector<std::pair<std::string, std::string> >    PC_ORDERED_MAP;
public:
    explicit TConfigSection() { Clear(); }
    ~TConfigSection() {}

    /**
    *@brief        是否为空
    */
    bool IsEmpty() { return m_SectionName.empty() && m_SectionValues.size() == 0; }

    /**
    *@brief        清理
    */
    void Clear() { m_SectionName = ""; m_SectionValues.clear(); }

    /**
    *@brief        设置section名称
    */
    void SetName(const char* szName)
    {
        if (szName != NULL) m_SectionName = szName;
    }

    /**
    *@brief        添加注释或空行
    */
    void AddComment(const char* szComment)
    {
        if (szComment == NULL)
        {
            return;
        }
        m_SectionValues.push_back(std::make_pair("", szComment));
    }

    /**
    *@brief        添加值
    */
    void AddValue(const char* szLine)
    {
        if (szLine == NULL)
        {
            return;
        }

        //实际数据
        size_t nEqualPos = 0;
        for (size_t i = 0; i < strlen(szLine); i++)
        {
            if (szLine[i] == '=')
            {
                nEqualPos = i;
                break;
            }
        }
        if (nEqualPos == 0)
        {
            //没找到等号或等号在第一个字符，当作注释处理
            m_SectionValues.push_back(std::make_pair("", szLine));
            return;
        }
        std::string first = TUtil::PCStrTrim(szLine, " \n\t\r", (int)nEqualPos);
        std::string second = TUtil::PCStrTrim(szLine + nEqualPos + 1, " \n\t\r");
        m_SectionValues.push_back(std::make_pair(first, second));
    }

    /**
    *@brief        写入值(覆盖所有同名key的value)
    */
    void SetValue(const char* pszKey, const char* pszValue)
    {
        bool bFind = false;
        for (PC_ORDERED_MAP::iterator it = m_SectionValues.begin(); it != m_SectionValues.end(); ++it)
        {
            //找到key，覆盖value
            if ((*it).first == pszKey)
            {
                bFind = true;
                (*it).second = pszValue;
            }
        }
        //没找到key，新建key=value
        if (!bFind)    m_SectionValues.push_back(std::make_pair(pszKey, pszValue));
    }

    /**
    *@brief        获取所有值
    */
    void GetAllValues(std::map<std::string, std::string>& mKeyValue)
    {
        //遍历key
        for (PC_ORDERED_MAP::iterator it = m_SectionValues.begin(); it != m_SectionValues.end(); ++it)
        {
            if ("" == (*it).first)
            {
                continue;
            }
            mKeyValue[(*it).first] = (*it).second;
        }
    }

    /**
    *@brief        获取值，如果有多个相同的值，获取最后的那个
    */
    std::string GetValue(const char* pszKey)
    {
        std::string result;
        if (pszKey == NULL || pszKey[0] == 0)
        {
            return result;
        }
        bool bFind = false;
        for (PC_ORDERED_MAP::iterator it = m_SectionValues.begin(); it != m_SectionValues.end(); ++it)
        {
            //找到key
            if ((*it).first == pszKey)
            {
                bFind = true;
                result = (*it).second;
            }
        }
        return result;
    }

public:
    //section name, 如果某个元素的section name  = ""，则其属于最开头的没有section的段，认为是一个section为空的段
    std::string     m_SectionName;
    //lisst的结构多个key=value，如果key=""，则此行为空行或注释
    PC_ORDERED_MAP  m_SectionValues;
};














ZL_TConfig::ZL_TConfig(const char* p_pszPath)
    :m_bParsed(false)
    , m_strFilePath(p_pszPath? p_pszPath:"")
{

}

std::string ZL_TConfig::GetCfg(const char* p_pszSec, const char* p_pszKey, const char* p_pszDefault )
{
    std::lock_guard<std::recursive_mutex> guard(m_CfgMutex);
    if (!this->OpenAndParse())
    {
        return p_pszDefault? p_pszDefault:"";
    }

    //将内部的配置文件map解析到mKeyValue
    std::string strSection;
    if (p_pszSec != NULL)
        strSection = p_pszSec;
    for (auto it = m_SectionList.begin(); it != m_SectionList.end(); ++it)
    {
        //找到section
        if ((*it)->m_SectionName == strSection)
        {
            //遍历key
            return (*it)->GetValue(p_pszKey);
        }
    }
    return p_pszDefault ? p_pszDefault : "";;
}

bool ZL_TConfig::SetCfg(const char* p_pszSec, const char* p_pszKey, const char* p_pszValue)
{
    std::lock_guard<std::recursive_mutex> guard(m_CfgMutex);
    if (!this->OpenAndParse())
    {
        return false;
    }
    if (!PushConfig(p_pszSec, p_pszKey, p_pszValue))
    {
        return false;
    }
    return  FlushToFile();
}

int  ZL_TConfig::GetCfg(const char* p_pszSec, const char* p_pszKey, int p_nDefault)
{
    auto ret = this->GetCfg(p_pszSec, p_pszKey, "");
    if (ret.empty())
    {
        return p_nDefault;
    }
    else
    {
        return  atoi(ret.c_str());
    }
}

bool ZL_TConfig::GetCfg(const char* p_pszSec, const char* p_pszKey, bool p_bDefault)
{
    auto ret = this->GetCfg(p_pszSec, p_pszKey, "");
    if (ret.empty())
    {
        return p_bDefault;
    }
    else
    {
        if (strcasecmp(ret.c_str(), "true") == 0)
            return true;
        else if (strcasecmp(ret.c_str(), "false") == 0)
            return false;
        else
            return p_bDefault;
    }
}

bool ZL_TConfig::SetCfg(const char* p_pszSec, const char* p_pszKey, int p_nValue)
{
    char szValue[64];
    sprintf(szValue, "%d", p_nValue);
    return this->SetCfg(p_pszSec, p_pszKey, szValue);
}

bool ZL_TConfig::SetCfg(const char* p_pszSec, const char* p_pszKey, bool p_bValue)
{
    if(p_bValue)    return this->SetCfg(p_pszSec, p_pszKey, "true");
    else            return this->SetCfg(p_pszSec, p_pszKey, "false");
}


std::map<std::string, std::string> ZL_TConfig::GetAllConfigs(const char* pszSec)
{
    std::lock_guard<std::recursive_mutex> guard(m_CfgMutex);
    std::map<std::string, std::string> ret;
    if (!this->OpenAndParse())
    {
        return ret;
    }

    //将内部的配置文件map解析到mKeyValue
    std::string strSection;
    if (pszSec != NULL)
        strSection = pszSec;
    for (auto it = m_SectionList.begin(); it != m_SectionList.end(); ++it)
    {
        //找到section
        if ((*it)->m_SectionName == strSection)
        {
            (*it)->GetAllValues(ret);
        }
    }
    return ret;
}

std::vector<std::string> ZL_TConfig::GetAllSectionNames()
{
    std::lock_guard<std::recursive_mutex> guard(m_CfgMutex);
    std::vector<std::string> ret;
    if (!this->OpenAndParse())
    {
        return ret;
    }
    for (auto it = m_SectionList.begin(); it != m_SectionList.end(); ++it)
    {
        //没有任何值的就没必要了
        if (!(*it)->m_SectionValues.empty())
            ret.push_back((*it)->m_SectionName);
    }
    return ret;
}

bool ZL_TConfig::OpenAndParse()
{
    if (m_bParsed)
    {
        return true;
    }
    m_SectionList.clear();

    //文件不存在时，也认为成功，认为是将文件内容解析到一个空的map中
#if defined(PCOS_WIN)
    if (::_access(m_strFilePath.c_str(), 0) != 0)
#else
    if (::access(m_strFilePath.c_str(), 0) != 0)
#endif
    {
        m_bParsed = true;
        return true;
    }

    //按行扫描文件
    std::shared_ptr<TConfigSection>  section(new TConfigSection());
    std::string     strMultiComment = "";
    auto result = TUtil::PCScanFileLine(m_strFilePath.c_str(), [&strMultiComment, &section, this](std::string& strLine)->void
        {
            std::string pRealLine = TUtil::PCStrTrim(strLine.c_str(), " \n\t\r");
            size_t nRealLineLen = pRealLine.length();

            //如果多行注释已经开始，则判断是否要结束
            if (!strMultiComment.empty())
            {
                strMultiComment += '\n';
                strMultiComment += pRealLine;
                if (std::string::npos != pRealLine.rfind("*/"))
                {
                    //多行注释结束
                    section->AddComment(strMultiComment.c_str());
                    strMultiComment.clear();
                }
                return;
            }

            //处理空行&单行注释
            if ((nRealLineLen == 0 || pRealLine[0] == ';' || pRealLine[0] == '#') ||
                (nRealLineLen > 1 && pRealLine[0] == '/' && pRealLine[1] == '/') ||
                (nRealLineLen > 3 && pRealLine[0] == '<' && pRealLine[1] == '!'))
            {
                section->AddComment(pRealLine.c_str());
                return;
            }

            //判断是不是多行注释
            if (strMultiComment.empty() && 0 == pRealLine.find("/*"))
            {
                //多行注释开始
                strMultiComment = pRealLine;
                if (std::string::npos != pRealLine.rfind("*/"))
                {
                    //多行注释结束
                    section->AddComment(strMultiComment.c_str());
                    strMultiComment.clear();
                }
                return;
            }

            //处理新的section
            if (pRealLine[0] == '[' && pRealLine[nRealLineLen - 1] == ']')
            {
                //将上一节的数据写进去
                m_SectionList.push_back(section);
                section.reset(new TConfigSection());

                //当前节名
                pRealLine[nRealLineLen - 1] = '\0';
                section->SetName(pRealLine.c_str() + 1);
                return;
            }

            //处理实际数据键值对
            section->AddValue(pRealLine.c_str());
            return;
        });
    if (!result)
    {
        return false;
    }

    //最后的多行注释没有闭合
    if (!strMultiComment.empty())
    {
        return false;
    }
    //将最后一节的数据写进去
    if (!section->IsEmpty())
    {
        m_SectionList.push_back(section);
    }
    m_bParsed = true;
    return result;
}


bool ZL_TConfig::PushConfig(const char* pszSec, const char* pszKey, const char* pszValue)
{
    if (pszKey == NULL || pszValue == NULL || pszKey[0] == 0)
    {
        return false;
    }

    //从文件映射的map中找
    std::string strSection;
    if (pszSec != NULL)
        strSection = pszSec;
    for (auto it = m_SectionList.begin(); it != m_SectionList.end(); ++it)
    {
        //找到section
        if ((*it)->m_SectionName == strSection)
        {
            (*it)->SetValue(pszKey, pszValue);
            return true;
        }
    }

    //没找到section，新建section
    std::shared_ptr<TConfigSection>  section(new TConfigSection());
    section->SetName(strSection.c_str());
    section->SetValue(pszKey, pszValue);
    m_SectionList.push_back(section);
    return true;
}


bool ZL_TConfig::FlushToFile()
{
    TUtil::PCCheckAndCreateDir(m_strFilePath.c_str());
    FILE* fp = fopen(m_strFilePath.c_str(), "w");
    if (NULL == fp)
    {
        return false;
    }
    for (auto itSec = m_SectionList.begin(); itSec != m_SectionList.end(); itSec++)
    {
        if ((*itSec)->m_SectionName != "")
        {
            fputs("[", fp);
            fputs((*itSec)->m_SectionName.c_str(), fp);
            fputs("]", fp);
            fputs("\n", fp);
        }
        for (auto itKV = (*itSec)->m_SectionValues.begin(); itKV != (*itSec)->m_SectionValues.end(); itKV++)
        {
            if ((*itKV).first != "")
            {
                fputs((*itKV).first.c_str(), fp);
                fputs("=", fp);
            }
            fputs((*itKV).second.c_str(), fp);
            fputs("\n", fp);
        }
    }
    fflush(fp);
    fclose(fp);
    return true;
}


////////////////////////////////////////////////////////////////////////////////////////////
static const char* const LOG_LEVEL_NAME[] = { "[D]", "[I]", "[W]", "[E]", "[O]" };

ZL_TLogger::ZL_TLogger(size_t buffsize)
    : m_eThreshold(ZL_TLogLevl::eINFO)
#if defined(PCOS_WIN)
    , m_strLogDir("D:/Log/")
#else
    , m_strLogDir("./Log/")
#endif
    , m_strLogFileName("Default")
    , m_strLogFileExt(".log")
    , m_nLogKeepMonth(3)
    , m_bPrintConsole(false)
    , m_bSplitBytes(true)
    , m_bLogCache(false)
    , m_bSimpleLog(true)
    , m_pLogFile(NULL)
    , m_pFmtBuff(NULL)
    , m_fmtBufSize(buffsize)
{
    m_pFmtBuff = new char[m_fmtBufSize];
    memset(m_pFmtBuff, 0, m_fmtBufSize);
    memset(&m_tmCurrent, 0, sizeof(m_tmCurrent));
    UpdateConfig();
}

ZL_TLogger::~ZL_TLogger()
{
    if(m_pLogFile)
        fclose(m_pLogFile);
    if (m_pFmtBuff)
        delete[] m_pFmtBuff;
}

ZL_TLogger* ZL_TLogger::Default()
{
    static ZL_TLogger o;
    return &o;
}

bool ZL_TLogger::SetLogConfig(const char* p_szModuleName, const char* p_szFilePath, const char* p_szSection )
{
    std::lock_guard<std::recursive_mutex> guard(m_Mutex);
    ZL_TConfig logCfg(p_szFilePath);
    auto strThreshold = logCfg.GetCfg(p_szSection, "Threshold", "INFO");
#if defined(PCOS_WIN)
    m_strLogDir = logCfg.GetCfg(p_szSection, "LogDir_W", "D:/Log/");
#else
    m_strLogDir = logCfg.GetCfg(p_szSection, "LogDir_L", "./Log/");
#endif
    m_strLogFileName = p_szModuleName ? p_szModuleName : "Default";
    m_strLogFileExt = logCfg.GetCfg(p_szSection, "LogFileExt", ".log");
    m_nLogKeepMonth = logCfg.GetCfg(p_szSection, "LogKeepMonth", 3);
    m_bPrintConsole = logCfg.GetCfg(p_szSection, "PrintConsole", false);
    m_bSplitBytes = logCfg.GetCfg(p_szSection, "SplitBytes", true);
    m_bLogCache = logCfg.GetCfg(p_szSection, "LogCache", false);
    m_bSimpleLog = logCfg.GetCfg(p_szSection, "SimpleLog", true);

    if (strThreshold[0] == 'D' || strThreshold[0] == 'd')
    {
        m_eThreshold = ZL_TLogLevl::eDEBUG;
    }
    else if (strThreshold[0] == 'I' || strThreshold[0] == 'i')
    {
        m_eThreshold = ZL_TLogLevl::eINFO;
    }
    else if (strThreshold[0] == 'W' || strThreshold[0] == 'w')
    {
        m_eThreshold = ZL_TLogLevl::eWARN;
    }
    else if (strThreshold[0] == 'E' || strThreshold[0] == 'e')
    {
        m_eThreshold = ZL_TLogLevl::eERROR;
    }
    else if (strThreshold[0] == 'O' || strThreshold[0] == 'o')
    {
        m_eThreshold = ZL_TLogLevl::eOFF;
    }
    TUtil::PCDirFormat(m_strLogDir);
    UpdateConfig();
    return true;
}

void ZL_TLogger::UpdateConfig()
{

}

void ZL_TLogger::ExecuteLogMaintenance()
{
    //目前只有清理过期日志这一个维护任务
    CleanHistoryLog();
}

void ZL_TLogger::WriteLogFmt(const char* p_pFileName, const char* p_pFuncName, unsigned int p_uLine, ZL_TLogLevl p_nLevel, const char* p_pszFmt, ...)
{
    if (p_pszFmt == NULL || p_nLevel >= ZL_TLogLevl::eOFF || p_nLevel < m_eThreshold)
    {
        return;
    }
    std::lock_guard<std::recursive_mutex> guard(m_Mutex);

    //检查Logger不通过不写
    struct tm tmValue;
    unsigned int uMsValue = 0;
    if (!TUtil::GetLocalTM(tmValue, uMsValue) || !CheckLogger(tmValue))
    {
        return;
    }

    //元信息处理
    const char* szLogLevel = LOG_LEVEL_NAME[(int)p_nLevel];
    const char* szShortFileName = TUtil::PCGetFileNameShort(p_pFileName);
    const char* szShortFuncName = TUtil::PCGetFuncNameShort(p_pFuncName);

    //格式化args
    va_list ap;
    va_start(ap, p_pszFmt);
#if defined(PCOS_WIN)
    if (-1 == _vsnprintf(m_pFmtBuff, m_fmtBufSize - 1, p_pszFmt, ap))
#else
    if (-1 == vsnprintf(m_pFmtBuff, m_fmtBufSize - 1, p_pszFmt, ap))
#endif
    {
        m_pFmtBuff[m_fmtBufSize - 1] = ('\0');
    }
    va_end(ap);

    //精简模式
    if (m_bSimpleLog)
    {
       // （精简模式：级别 + 时间 + 文件名 + 函数名 + 日志内容 ）
        fprintf(m_pLogFile, "%s[%02d:%02d:%02d.%03u][%s:%s] %s\n", szLogLevel,
            tmValue.tm_hour, tmValue.tm_min, tmValue.tm_sec, uMsValue,
            szShortFileName, szShortFuncName, m_pFmtBuff);
        
        if(m_bPrintConsole) 
        {
            CPCAutoConsoleColor autoClolr(p_nLevel);
            printf("%s[%02d:%02d:%02d.%03u][%s:%s] %s\n", szLogLevel,
                tmValue.tm_hour, tmValue.tm_min, tmValue.tm_sec, uMsValue,
                szShortFileName, szShortFuncName, m_pFmtBuff);
        }
    }
    else
    {
        // （完整模式：级别 + 时间 + 线程号 + 文件名 + 函数名 + 行号 + 日志内容）
        int nThradID = TUtil::PCGetTID();
        fprintf(m_pLogFile, "%s[%02d:%02d:%02d.%03u][%05d][%s:%s:%u] %s\n", szLogLevel,
            tmValue.tm_hour, tmValue.tm_min, tmValue.tm_sec, uMsValue,
            nThradID, szShortFileName, szShortFuncName, p_uLine, m_pFmtBuff);
        
        if (m_bPrintConsole)
        {
            CPCAutoConsoleColor autoClolr(p_nLevel);
            printf("%s[%02d:%02d:%02d.%03u][%05d][%s:%s:%u] %s\n", szLogLevel,
                tmValue.tm_hour, tmValue.tm_min, tmValue.tm_sec, uMsValue,
                nThradID, szShortFileName, szShortFuncName, p_uLine, m_pFmtBuff);
        }
    }
    
    //WARN以上级别触发立即写入 & 配置为立即写入
    if (p_nLevel > ZL_TLogLevl::eINFO || !m_bLogCache)
    {
        fflush(m_pLogFile);
    }
}

void ZL_TLogger::WriteLogBytes(const char* p_pFileName, const char* p_pFuncName, unsigned int p_uLine, ZL_TLogLevl p_nLevel, const char* p_pszTips, const void* p_pszBytes, size_t p_nBytesLen)
{
    if (p_pszTips == NULL || p_nLevel >= ZL_TLogLevl::eOFF || p_nLevel < m_eThreshold)
    {
        return;
    }
    std::lock_guard<std::recursive_mutex> guard(m_Mutex);

    //检查Logger不通过不写
    struct tm tmValue;
    unsigned int uMsValue = 0;
    if (!TUtil::GetLocalTM(tmValue, uMsValue) || !CheckLogger(tmValue))
    {
        return;
    }

    //元信息处理
    const char* szLogLevel = LOG_LEVEL_NAME[(int)p_nLevel];
    const char* szShortFileName = TUtil::PCGetFileNameShort(p_pFileName);
    const char* szShortFuncName = TUtil::PCGetFuncNameShort(p_pFuncName);
    std::string hexStr = TUtil::PCBytes2Dsp(p_pszBytes, p_nBytesLen, m_bSplitBytes ? " " : "");

    //精简模式
    if (m_bSimpleLog)
    {
        // （精简模式：级别 + 时间 + 文件名 + 函数名 + 日志内容 ）
        fprintf(m_pLogFile, "%s[%02d:%02d:%02d.%03u][%s:%s] %s %s\n", szLogLevel,
            tmValue.tm_hour, tmValue.tm_min, tmValue.tm_sec, uMsValue,
            szShortFileName, szShortFuncName, p_pszTips, hexStr.c_str());

        if (m_bPrintConsole)
        {
            CPCAutoConsoleColor autoClolr(p_nLevel);
            printf("%s[%02d:%02d:%02d.%03u][%s:%s] %s %s\n", szLogLevel,
                tmValue.tm_hour, tmValue.tm_min, tmValue.tm_sec, uMsValue,
                szShortFileName, szShortFuncName, p_pszTips, hexStr.c_str());
        }
    }
    else
    {
        // （完整模式：级别 + 时间 + 线程号 + 文件名 + 函数名 + 行号 + 日志内容）
        int nThradID = TUtil::PCGetTID();
        fprintf(m_pLogFile, "%s[%02d:%02d:%02d.%03u][%05d][%s:%s:%u] %s %s\n", szLogLevel,
            tmValue.tm_hour, tmValue.tm_min, tmValue.tm_sec, uMsValue,
            nThradID, szShortFileName, szShortFuncName, p_uLine, p_pszTips, hexStr.c_str());

        if (m_bPrintConsole)
        {
            CPCAutoConsoleColor autoClolr(p_nLevel);
            printf("%s[%02d:%02d:%02d.%03u][%05d][%s:%s:%u] %s %s\n", szLogLevel,
                tmValue.tm_hour, tmValue.tm_min, tmValue.tm_sec, uMsValue,
                nThradID, szShortFileName, szShortFuncName, p_uLine, p_pszTips, hexStr.c_str());
        }
    }

    //WARN以上级别触发立即写入 & 配置为立即写入
    if (p_nLevel > ZL_TLogLevl::eINFO || !m_bLogCache)
    {
        fflush(m_pLogFile);
    }
}

bool ZL_TLogger::CheckLogger(struct tm& tmValue)
{
    //如果文件已经打开且日期相同，无需任何操作
    if (m_pLogFile != NULL && 
        (tmValue.tm_mday == m_tmCurrent.tm_mday && tmValue.tm_mon == m_tmCurrent.tm_mon && tmValue.tm_year == m_tmCurrent.tm_year))
    {
        return true;
    }
    m_tmCurrent = tmValue;

    //关闭已有文件
    if (m_pLogFile)
    {
        fclose(m_pLogFile);
        m_pLogFile = NULL;
    }

    //顺便执行日志维护任务
    ExecuteLogMaintenance();

    //格式化日期文件串
    char szDir[260] = { 0 };
    char szFullPath[260] = { 0 };
    sprintf(szDir, "%s%04d-%02d-%02d/", m_strLogDir.c_str(),
        m_tmCurrent.tm_year + 1900, m_tmCurrent.tm_mon + 1, m_tmCurrent.tm_mday);
    sprintf(szFullPath, "%s%s.%04d-%02d-%02d%s", szDir,m_strLogFileName.c_str(), 
        m_tmCurrent.tm_year + 1900, m_tmCurrent.tm_mon + 1, m_tmCurrent.tm_mday,
        m_strLogFileExt.c_str());

    //确保路径存在
    auto result = TUtil::PCCreateDir(szDir);
    if (!result)
    {
        printf("PCCheckAndCreateDir(%s) fail.err!!!!!!!!!!!!!!!!!!!1\n", szDir);
        return false;
    }

    //(重新)打开文件
    m_pLogFile = fopen(szFullPath, "ab+");
    if (NULL == m_pLogFile)
    {
        printf("fopen(%s) fail.err:%d!!!!!!!!!!!!!!!!!!!!\n", szFullPath, (int)PCGetLastError());
        return false;
    }
    return true;
}

void ZL_TLogger::CleanHistoryLog() 
{
    //把月换算成天
    int nLogKeepDay = this->m_nLogKeepMonth * 30;
    if (nLogKeepDay < 0)
        return;

    //从-nLogKeepDay天~-nLogKeepDay-365，这个范围的日志删除
    int year = m_tmCurrent.tm_year + 1900;
    int month = m_tmCurrent.tm_mon + 1;
    int day = m_tmCurrent.tm_mday;
    TUtil::AddDay(year, month, day, -nLogKeepDay);

    auto szLogDir = m_strLogDir.c_str();
    auto szLogFileName = m_strLogFileName.c_str();
    auto szLogFileExt = m_strLogFileExt.c_str();
    
    char szDir[260] = { 0 };
    char szFullPath[260] = { 0 };
    for (int i = 0; i < 365; i++)
    {
        TUtil::AddDay(year, month, day, -1);

        sprintf(szDir, "%s%04d-%02d-%02d/", szLogDir, year, month, day);
        sprintf(szFullPath, "%s%s.%04d-%02d-%02d%s", szDir, szLogFileName, year, month, day, szLogFileExt);

        //文件夹不存在，不处理
#if defined(PCOS_WIN)
        if (::_access(szDir, 0) != 0)
#else
        if (::access(szDir, 0) != 0)
#endif
        {
            continue;
        }

        //删除文件
        if (0 != ::remove(szFullPath))
        {
            //printf("remove(%s) fail.err:%d!!!!!!!!!!!!!!!!!!!!\n", szFullPath, (int)PCGetLastError());
        }

        //如果文件夹为空则删除，不为空会自动删除失败
        if (0 != ::rmdir(szDir))
        {
            //printf("rmdir(%s) fail.err:%d!!!!!!!!!!!!!!!!!!!!\n", szDir, (int)PCGetLastError());
        }
    }
   
}